package com.webex.ta.hydra.gui.wm;

import com.webex.ta.hydra.gui.execution.Invoker;

import javax.swing.*;
import java.util.ArrayList;
import java.util.List;

/**
 * Created by Cisco WebEx.
 * User: vegaz
 * Date: 2010-10-21
 * Time: 9:38:18
 */
public class InstructionProcessor implements Runnable {
    private final Object lock = new Object();
    private final List<InstructionGroup> todos = new ArrayList<InstructionGroup>();
    private int count;

    public final int getCount() {
        return count;
    }

    public void execute(final List<Instruction> instructions) {
        synchronized(lock) {
            final boolean isBusy = count > 0;

            final InstructionGroup instructionGroup = new InstructionGroup(instructions);
            todos.add(instructionGroup);
            count += instructions.size();

            if (!isBusy) {
                run();
            }
        }
    }

    public void run() {
        synchronized(lock) {
            final InstructionGroup instructionGroup = getNextInstructionGroup();
            if (instructionGroup == null || instructionGroup.isEmpty())
                return;

            final Instruction instruction = instructionGroup.takeNextInstruction();
            count--;
            final boolean hasQueued = count > 0;
            Invoker.invoke(instruction).doAfterDone(new Runnable() {
                public void run() {
                    if (hasQueued)
                        InstructionProcessor.this.run();
                }
            });
            SwingUtilities.invokeLater(instruction);
        }
    }

    private InstructionGroup getNextInstructionGroup() {
        while (!todos.isEmpty()) {
            InstructionGroup candidate = todos.get(0);
            if (!candidate.instructions.isEmpty())
                return candidate;
            todos.remove(candidate);
        }
        return null;
    }

    private static class InstructionGroup {
        private final List<Instruction> instructions;

        private InstructionGroup(List<Instruction> instructions) {
            this.instructions = instructions;
        }

        public boolean isEmpty() {
            return instructions.isEmpty();
        }

        public Instruction takeNextInstruction() {
            Instruction instruction = instructions.remove(0);
            return instruction;
        }

    }
}
